#include "OrbitCamera.h"
#include "MathHelper.h"
#include "KeyboardManager.h"
#include "MouseManager.h"
#include "MatrixHelper.h"

OrbitCamera::OrbitCamera()
{
	m_fPitch			= ORBITCAMERA_PITCH_DEFAULT;
	m_fPitchTarget		= ORBITCAMERA_PITCH_DEFAULT;
	m_fYaw				= 0;
	m_fYawTarget 		= 0;
	m_fDistance			= 0;
	m_fDistanceTarget	= ORBITCAMERA_DISTANCE_DEFAULT;
}

OrbitCamera::~OrbitCamera()
{
}

void OrbitCamera::Update(float elapsedTime)
{
	KeyboardManager* keyboardManager = KeyboardManager::GetInstance();
	MouseManager* mouseManager = MouseManager::GetInstance();

	Vector2i mousePositionChange = mouseManager->GetPositionChange();
	int mouseWheelChange = mouseManager->GetWheelChange();

	if(keyboardManager->IsKeyPressed(GLFW_KEY_SPACE))
	{
		ResetRotation();
	}

	if(mouseManager->IsButtonDown(MOUSEBUTTON_RIGHT))
	{
		if(mousePositionChange.X)
		{
			m_fYawTarget += mousePositionChange.X * elapsedTime * ORBITCAMERA_MOUSE_SENSITIVITY;
		}
		if(mousePositionChange.Y)
		{
			m_fPitchTarget += mousePositionChange.Y * elapsedTime * ORBITCAMERA_MOUSE_SENSITIVITY;
		}
	}

	if(mouseWheelChange != 0)
	{
		if(mouseWheelChange > 0)
		{
			m_fDistanceTarget += ORBITCAMERA_DISTANCE_STEP;
		}
		else
		{
			m_fDistanceTarget -= ORBITCAMERA_DISTANCE_STEP;
		}
	}

	Clamp(m_fDistanceTarget, ORBITCAMERA_DISTANCE_MINIMUM, ORBITCAMERA_DISTANCE_MAXIMUM);
	Clamp(m_fPitchTarget, ORBITCAMERA_PITCH_MINIMUM, ORBITCAMERA_PITCH_MAXIMUM);

	if(m_fDistance != m_fDistanceTarget)
	{
		float distanceDelta = m_fDistanceTarget - m_fDistance;

		m_fDistance += distanceDelta * ORBITCAMERA_DISTANCE_RAMP * elapsedTime;

		if(abs(distanceDelta) < 0.015625f)	// TODO: No magic numbers.
		{
			m_fDistance = m_fDistanceTarget;
		}
	}

	if(m_fPitch != m_fPitchTarget)
	{
		float pitchDelta = m_fPitchTarget - m_fPitch;

		m_fPitch += pitchDelta * ORBITCAMERA_PITCH_RAMP * elapsedTime;

		if(abs(pitchDelta) < 0.0009765625f)	// TODO: No magic numbers.
		{
			m_fPitch = m_fPitchTarget;
		}
	}

	if(m_fYaw != m_fYawTarget)
	{
		float yawDelta = m_fYawTarget - m_fYaw;

		m_fYaw += yawDelta * ORBITCAMERA_YAW_RAMP * elapsedTime;

		if(abs(yawDelta) < 0.0009765625f)	// TODO: No magic numbers.
		{
			m_fYaw = m_fYawTarget;
		}
	}

	Clamp(m_fDistance, ORBITCAMERA_DISTANCE_MINIMUM, ORBITCAMERA_DISTANCE_MAXIMUM);
	Clamp(m_fPitch, ORBITCAMERA_PITCH_MINIMUM, ORBITCAMERA_PITCH_MAXIMUM);

	Quaternion rotation;

	rotation *= Quaternion::CreateFromAxisAngle(Vector3f::Left, m_fPitch);
	rotation *= Quaternion::CreateFromAxisAngle(Vector3f::Up, m_fYaw);
	rotation *= GetParent()->GetRotation();

	SetRotation(rotation);

	Vector3f origin;
	
	if(HasParent())
	{
		origin = GetParent()->GetPosition();
	}

	Matrix matrix;
	Vector3f forward;

	MatrixHelper::SetRotation(matrix, rotation);
	MatrixHelper::GetForward(matrix, forward);

	Vector3f position = origin + -forward * m_fDistance;

	SetPosition(position);

	Camera::Update(elapsedTime);
}

void OrbitCamera::ResetRotation()
{
	// TODO: Not very good.  Gotta be a better way.
	int rotations = m_fYaw / (2 * PI);

	m_fYaw -= rotations * (2 * PI);

	m_fPitchTarget = ORBITCAMERA_PITCH_DEFAULT;
	m_fYawTarget = 0.0f;
}

void OrbitCamera::ResetDistance()
{
	m_fDistanceTarget = ORBITCAMERA_DISTANCE_DEFAULT;
}